package cc.mrbird.system.service.impl;

import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

import cc.mrbird.common.domain.QueryRequest;
import cc.mrbird.system.dao.DeptMapper;
import cc.mrbird.system.dao.RoleMapper;
import cc.mrbird.system.domain.*;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.entity.result.ExcelVerifyHandlerResult;
import cn.afterturn.easypoi.handler.inter.IExcelVerifyHandler;
import com.google.common.math.Stats;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cc.mrbird.common.service.impl.BaseService;
import cc.mrbird.common.util.MD5Utils;
import cc.mrbird.system.dao.UserMapper;
import cc.mrbird.system.dao.UserRoleMapper;
import cc.mrbird.system.service.UserRoleService;
import cc.mrbird.system.service.UserService;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletResponse;


@Service("userService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UserServiceImpl extends BaseService<User> implements UserService {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserMapper userMapper;

    /**
     * 角色中间表
     */
    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private UserRoleService userRoleService;

    /**
     * 用户的服务接口
     */
    @Autowired
    private UserService userService;

    /**
     * 部门的服务接口
     */
    @Autowired
    private DeptMapper deptMapper;

    /**
     * 角色服务接口
     */
    @Autowired
    private RoleMapper roleMapper;


    @Override
    public User findByName(String userName) {
        Example example = new Example(User.class);
        example.createCriteria().andCondition("lower(username)=", userName.toLowerCase());
        List<User> list = this.selectByExample(example);
        return list.isEmpty() ? null : list.get(0);
    }

    @Override
    public List<User> findUserWithDept(User user, QueryRequest request) {
        try {
            List<User> userWithDept = this.userMapper.findUserWithDept(user);
            return userWithDept;

        } catch (Exception e) {
            log.error("error", e);
            return new ArrayList<>();
        }
    }

    @Override
    @Transactional
    public void registUser(User user) {
        user.setCrateTime(new Date());
        user.setTheme(User.DEFAULT_THEME);
        user.setAvatar(User.DEFAULT_AVATAR);
        user.setSsex(User.SEX_UNKNOW);
        user.setPassword(MD5Utils.encrypt(user.getUsername(), user.getPassword()));
        this.save(user);
        UserRole ur = new UserRole();
        ur.setUserId(user.getUserId());
        ur.setRoleId(3L);
        this.userRoleMapper.insert(ur);
    }

    @Override
    @Transactional
    public void updateTheme(String theme, String userName) {
        Example example = new Example(User.class);
        example.createCriteria().andCondition("username=", userName);
        User user = new User();
        user.setTheme(theme);
        this.userMapper.updateByExampleSelective(user, example);
    }

    /**
     * 新增用户
     *
     * @param user  用户名称
     * @param roles 角色名称
     */
    @Override
    @Transactional
    public void addUser(User user, Long[] roles) {
        user.setCrateTime(new Date());
        user.setTheme(User.DEFAULT_THEME);
        user.setAvatar(User.DEFAULT_AVATAR);
        user.setPassword(MD5Utils.encrypt(user.getUsername(), user.getPassword()));
        this.save(user);
        setUserRoles(user, roles);
    }

    private void setUserRoles(User user, Long[] roles) {
        Arrays.stream(roles).forEach(roleId -> {
            UserRole ur = new UserRole();
            ur.setUserId(user.getUserId());
            ur.setRoleId(roleId);
            this.userRoleMapper.insert(ur);
        });
    }

    @Override
    @Transactional
    public void updateUser(User user, Long[] roles) {
        user.setPassword(null);
        user.setUsername(null);
        user.setModifyTime(new Date());
        this.updateNotNull(user);
        Example example = new Example(UserRole.class);
        example.createCriteria().andCondition("user_id=", user.getUserId());
        this.userRoleMapper.deleteByExample(example);
        setUserRoles(user, roles);
    }

    @Override
    @Transactional
    public void deleteUsers(String userIds) {
        List<String> list = Arrays.asList(userIds.split(","));
        this.batchDelete(list, "userId", User.class);

        this.userRoleService.deleteUserRolesByUserId(userIds);
    }

    @Override
    @Transactional
    public void updateLoginTime(String userName) {
        Example example = new Example(User.class);
        example.createCriteria().andCondition("lower(username)=", userName.toLowerCase());
        User user = new User();
        user.setLastLoginTime(new Date());
        this.userMapper.updateByExampleSelective(user, example);
    }

    @Override
    @Transactional
    public void updatePassword(String password) {
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        Example example = new Example(User.class);
        example.createCriteria().andCondition("username=", user.getUsername());
        String newPassword = MD5Utils.encrypt(user.getUsername().toLowerCase(), password);
        user.setPassword(newPassword);
        this.userMapper.updateByExampleSelective(user, example);
    }

    @Override
    public UserWithRole findById(Long userId) {
        List<UserWithRole> list = this.userMapper.findUserWithRole(userId);
        List<Long> roleList = list.stream().map(UserWithRole::getRoleId).collect(Collectors.toList());
        if (list.isEmpty()){
            return null;
        }
        UserWithRole userWithRole = list.get(0);
        userWithRole.setRoleIds(roleList);
        return userWithRole;
    }

    @Override
    public User findUserProfile(User user) {
        return this.userMapper.findUserProfile(user);
    }

    @Override
    @Transactional
    public void updateUserProfile(User user) {
        user.setUsername(null);
        user.setPassword(null);
        if (user.getDeptId() == null)
            user.setDeptId(0L);
        this.updateNotNull(user);
    }

    /**
     * 用户文件上传
     *
     * @param importFactory 文件信息
     * @param response      请求
     * @return
     */
    @Override
    public List<UserImport> importAllProjectEntry(MultipartFile importFactory, HttpServletResponse response) throws Exception {
        //set集合 用于判断自身是否重复
        Set set = new HashSet();
        //导入集合
        List<UserImport> list = new ArrayList();
        //导入对象
        UserImport userImport = new UserImport();
        //导入函数对象
        ImportParams importParams = new ImportParams();
        //表头行数
        importParams.setHeadRows(1);
        //表头标题行
        importParams.setTitleRows(0);
        //数据是否需要效验
        importParams.setNeedVerfiy(true);
        //自定义效应
        InputStream inputStream = importFactory.getInputStream();
        //自定义效验
        importParams.setVerifyHandler(new IExcelVerifyHandler<UserImport>() {
            @Override
            public ExcelVerifyHandlerResult verifyHandler(UserImport o) {
                StringBuilder str = new StringBuilder();
                //用户效验 用户是唯一的
                if (StringUtils.isNotEmpty(o.getUsername())) {
                    Boolean byName = ifRepetition(o.getUsername());
                    //判断对象是否为空不为空 不为空这已存在这个用户
                    if (byName) {
                        str.append("用户名（工号已存在）");
                    }

                    //判断导入的时候有没有出现重复的
                    Boolean ifStates = ifRepeat(o.getUsername(),set);
                    //判断用户表是否出现重复的
                    if (ifStates) {
                        str.append("自身文件出现重复的用户名或（工号）");
                    }

                }
                //效验部门是否存在
                if (StringUtils.isNotEmpty(o.getDeptName())) {
                    if (ifDeft(o.getDeptName())) {
                        str.append("部门不存在");
                    }
                }
                //效验角色是否存在
                if (StringUtils.isNotEmpty(o.getRoleName())) {
                    if (ifRole(o.getRoleName())) {
                        str.append("角色不存在");
                    }
                }
                //返回效应错误信息
                return new ExcelVerifyHandlerResult(StringUtils.isEmpty(str.toString()), str.toString());
            }
        });
        //载入流
        ExcelImportResult<UserImport> userImportExcelImportResult = ExcelImportUtil.importExcelMore(inputStream, UserImport.class, importParams);
        //效验成功的数据
        List<UserImport> successList = userImportExcelImportResult.getList();
        //效应失败的数据
        List<UserImport> failList = userImportExcelImportResult.getFailList();
        //如果有效验失败的数据就放会给客户
        if (failList.size() > 0) {
            //导出错误信息
            return failList;
            //如果没有错误信息就存在数据库里面
        } else {
            //没有报错的数据存在数据库里面
            Boolean states = storedValue(successList);
        }
        return failList;
    }

    /**
     * 判断自身的文件 用户(工号)有没有出现重复的
     *
     * @param username 用户名称
     * @param set
     * @return
     */
    private Boolean ifRepeat(String username, Set set) {
        //判断有没有包含次元素
        boolean contains = set.contains(username);
        if (contains) {
            return true;
        } else {
            //没有重复的话就就直接添加 用户下次做判断
            set.add(username);
            return false;
        }
    }

    /**
     * 没有报错的信息存在数据库里面
     *
     * @param successList 文件上传的数据
     * @return 是否成功
     */
    @Transactional
    public Boolean storedValue(List<UserImport> successList) {
        //状态
        Boolean states = false;
        //说明总共有四张表 一张中间表
        // 先获取部门全部信息
        List<Dept> deptList = deptMapper.selectAll();
        //只要id 跟 部门名称存在hamp 方便取值
        HashMap<Long, String> deptMap = new HashMap<>();
        //id 跟部门存集合
        deptList.forEach(obj -> {
            // ID 部门名称
            deptMap.put(obj.getDeptId(), obj.getDeptName());
        });

        //存入角色的中间表
        //先查出全部的角色名称
        List<Role> roles = roleMapper.selectAll();
        //存入角色id 名称
        HashMap<Long, String> roleMap = new HashMap<>();
        roles.forEach(obj -> {
            //角色id 角色名称
            roleMap.put(obj.getRoleId(), obj.getRoleName());
        });

        //存数据user user后面批量插入
        List<User> userList = new ArrayList<>();

        //角色迭代
        Set<Map.Entry<Long, String>> rolesEntries = roleMap.entrySet();

        //部门
        Set<Map.Entry<Long, String>> depEntries = deptMap.entrySet();

        //2获取主表信息先存主表信息
        successList.forEach(obj -> {
            //用户
            User user = new User();
            //转换
            BeanUtils.copyProperties(obj, user);
            //角色
            for (Map.Entry<Long, String> rolesEntry : rolesEntries) {
                if (obj.getRoleName().equals(rolesEntry.getValue())) {
                    //角色id
                    user.setRoleID(rolesEntry.getKey());
                    //跳出循环
                    break;
                }
            }

            //部门
            for (Map.Entry<Long, String> depEntry : depEntries) {
                if (obj.getDeptName().equals(depEntry.getValue())) {
                    //部门id
                    user.setDeptId(depEntry.getKey());
                    //跳出循环
                    break;
                }
            }

            //默认
            user.setCrateTime(new Date());
            user.setTheme(User.DEFAULT_THEME);
            user.setAvatar(User.DEFAULT_AVATAR);
            //状态是否有效
            user.setStatus(User.STATUS_VALID);
            //默认密码
            user.setPassword(MD5Utils.encrypt(user.getUsername(), "123456"));
            //存入集合
            userList.add(user);
        });
        //存入数据库
        int i = userMapper.insertList(userList);
        //批量插入成功
        if (i > 0) {
            //插入第三张表
            //角色中间表
            List<UserRole> userRolesList = new ArrayList<>();
            for (User user : userList) {
                UserRole userRole = new UserRole();
                //主表id
                userRole.setUserId(user.getUserId());
                //角色id
                userRole.setRoleId(user.getRoleID());
                userRolesList.add(userRole);
            }
            //批量添加到中间表
            int t = userRoleMapper.insertList(userRolesList);
            if (t > 0) {
                states = true;
            }
        }

        return states;

    }

    /**
     * 导出错误信息返回给客户
     *
     * @param failList 错误信息
     * @param response 响应
     */
    private void exportError(List<UserImport> failList, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> dataList = new ArrayList<>();
        //String fileName ="用户名称导入失败详情"+ new SimpleDateFormat("yyyyMMdd").format(new Date());
        String fileName = UUID.randomUUID().toString() + ".xls";
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.addHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes("UTF-8"), "ISO8859-1"));
        fileName = UUID.randomUUID().toString() + ".xls";
        response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
        ExportParams params = new ExportParams();
        params.setSheetName("");
        params.setTitle("用户导入不合法数据" + failList.size() + "");
        params.setAddIndex(true);
        Map map = new HashMap();
        map.put("title", params);
        map.put("entity", UserImport.class);
        map.put("data", failList);
        dataList.add(map);
        Workbook workbook = ExcelExportUtil.exportExcel(dataList, ExcelType.HSSF);
        OutputStream outputStream = response.getOutputStream();
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        workbook.write(bufferedOutputStream);
        bufferedOutputStream.flush();
        bufferedOutputStream.close();
        outputStream.close();

    }

    /**
     * 导出错误信息
     *
     * @param failList 错误信息
     * @param response 响应
     */
    private void outPutExcel(List<UserImport> failList, HttpServletResponse response) throws IOException {
        // 每次写100行数据，就刷新数据出缓存
        SXSSFWorkbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory,
        Sheet sh = wb.createSheet();
        String[] titles = {"用户", "部门"};
        Row row = sh.createRow(0);
        // 第一行设置标题
        for (int i = 0; i < titles.length; i++) {
            String title = titles[i];
            Cell cell1 = row.createCell(i);
            cell1.setCellValue(title);
        }

        // 导出数据
        for (int rowNum = 0; rowNum < failList.size(); rowNum++) {

            Row rowData = sh.createRow(rowNum + 1);
            // TbClass 这个是我的业务类，这个是根据业务来进行填写数据
            UserImport userImport = failList.get(rowNum);
            // 第一列
            Cell cellDataA = rowData.createCell(0);
            cellDataA.setCellValue(userImport.getUsername());
            // 第二列
            Cell cellDataB = rowData.createCell(1);
            cellDataB.setCellValue(userImport.getErrorMsg());
        }

        String fileName = "文件名称.xlsx";
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        wb.write(response.getOutputStream());
        wb.close();

    }


    /**
     * 效验用户存不存在
     *
     * @param ByName 用户名称（工号）
     * @return
     */
    private Boolean ifRepetition(String ByName) {
        //sql构造器
        Example example = new Example(User.class);
        //用户名称
        example.createCriteria().andCondition("USERNAME=", ByName.trim());
        //判断用户名称是否存在
        int count = userMapper.selectCountByExample(example);
        //存在
        if (count > 0) {
            return true;
            //不存在
        } else {
            return false;
        }

    }

    /**
     * 效验部门存不存在
     *
     * @param byDeft 部门的名称
     * @return
     */
    private Boolean ifDeft(String byDeft) {
        //sql构造器
        Example example = new Example(Dept.class);
        //用户名称
        example.createCriteria().andCondition("DEPT_NAME=", byDeft.trim());
        //判断用户名称是否存在
        int count = deptMapper.selectCountByExample(example);
        //存在
        if (count > 0) {
            return false;
            //不存在
        } else {
            return true;
        }

    }

    /**
     * 效验角色存不存在
     *
     * @param byRole 角色的名称
     * @return
     */
    private Boolean ifRole(String byRole) {
        //sql构造器
        Example example = new Example(Role.class);
        //用户名称
        example.createCriteria().andCondition("ROLE_NAME=", byRole.trim());
        //判断用户名称是否存在
        int count = roleMapper.selectCountByExample(example);
        //存在
        if (count > 0) {
            return false;
            //不存在
        } else {
            return true;
        }

    }


}
